package com.gitee.wsl.math.vector

import com.gitee.wsl.ext.base.println
import com.gitee.wsl.collections.list.BaseList
import kotlin.math.sqrt

/**
 * 通过索引数组和值数组表示的稀疏向量。
 *
 * @param size 向量的大小。
 * @param indices 索引数组，假设严格递增。
 * @param values 值数组，必须与索引数组长度相同。
 */

class SparseVector  (
    override val size: Int,
     val indices: IntArray,
     val values: DoubleArray) : Vector,BaseList<Double> {

    // 验证数据的有效性
    init{
        require(size >= 0) { "请求的稀疏向量的大小必须不小于0。" }
        require(indices.size == values.size) {
            "稀疏向量要求索引的维度与值的维度匹配。提供了" + "${indices.size}个索引和${values.size}个值。"
        }
        require(indices.size <= size
        ) { "提供了${indices.size}个索引和值，超过了指定的向量大小${size}。" }

        if (indices.isNotEmpty()) {
            require(indices[0] >= 0) { "发现负索引：${indices[0]}。" }
        }
        var prev = -1
        indices.forEach { i ->
            require(prev < i) { "索引 $i 跟随 $prev，并且不是严格递增的。" }
            prev = i
        }
        require(prev < size) { "索引$prev 超出了向量的大小 $size。" }
    }

    override fun toString(): String = "($size,${indices.println()},${values.println()})"

    override fun toArray(): DoubleArray  {
        val data = DoubleArray(size){i-> 0.0 }

        indices.forEach { i->
            data[indices[i]] = values[i]
        }

        /*var i = 0
        val nnz = indices.size
        while (i < nnz) {
            data(indices(i)) = values(i)
            i += 1
        }*/
       return data
    }

    override fun copy(): SparseVector {
      return SparseVector(size, indices.copyOf(), values.copyOf())
    }

    //private override def asBreeze: BV[Double] = new BSV[Double](indices, values, size)

   /* override fun apply(i: Int): Double  {
        if (i < 0 || i >= size) {
            throw  IndexOutOfBoundsException("索引$i 超出了范围[0, $size)")
        }
        val j = Arrays.binarySearch(indices, i)
        return if (j < 0) 0.0 else values[j]
    }*/

    override fun foreachActive(f: (Int, Double) -> Unit){
        var i = 0
        val localValuesSize = values.size
        val localIndices = indices
        val localValues = values

        while (i < localValuesSize) {
            f(localIndices[i], localValues[i])
            i += 1
        }
    }

    //override fun equals(other: Any?): Boolean = super.equals(other)

    override fun hashCode(): Int {
        var result: Int = 31 + size
        val end = values.size
        var k = 0
        var nnz = 0
        while (k < end && nnz < MAX_HASH_NNZ) {
            val v = values[k]
            if (v != 0.0) {
                val i = indices[k]
                result = 31 * result + i
                //val bits = java.lang.Double.doubleToLongBits(v)
                //result = 31 * result + (bits ^ (bits >>> 32)).toInt()
                nnz += 1
            }
            k += 1
        }
        return result
    }

    override val numActives: Int = values.size

    override val numNonzeros: Int
        get() {
        var nnz = 0
        values.forEach { v ->
            if (v != 0.0) {
                nnz += 1
            }
        }
        return nnz
    }

     override fun toSparseWithSize(nnz: Int): SparseVector {
        // 如果指定的非零元素数量与当前向量的非零元素数量相同，则返回当前向量
        return if (nnz == numActives) {
            this
        } else {
            val ii = IntArray(nnz)
            val vv = DoubleArray(nnz)
            var k = 0
            foreachActive { i, v ->
                // 只保留非零元素的索引和值
                if (v != 0.0) {
                    ii[k] = i
                    vv[k] = v
                    k += 1
                }
            }
            SparseVector(size, ii, vv)
        }
    }

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other !is SparseVector) return false

        if (size != other.size) return false
        if (!indices.contentEquals(other.indices)) return false
        return values.contentEquals(other.values)
    }

    override fun get(index: Int): Double {
        checkIndex(index)
        val j = indices.indexOf(index)
        return if (j < 0) 0.0 else values[j]

        /*val j = Arrays.binarySearch(indices, index)
        return if (j < 0) 0.0 else values[j]*/
    }

    override fun set(index: Int, value: Double) {
        checkIndex(index)
        val j = indices.indexOf(index)
        values[j]=value

        //val j = Arrays.binarySearch(indices, index)
        //values[j]=value
    }

    private fun checkIndex(index: Int){
        /*if (index < 0 || index >= size) {
            throw  IndexOutOfBoundsException("索引$index 超出了范围[0, $size)")
        }*/
        require(index in 0..size){"索引$index 超出了范围[0, $size)"}
    }



    override val argmax: Int
        get() {
        return if (size == 0) {
            -1
        } else if (numActives == 0) {
            0
        } else {
            // 找到最大的非零元素
            var maxIdx = indices[0]
            var maxValue = values[0]
            var maxJ = 0
            var j = 1
            val na = numActives
            while (j < na) {
                val v = values[j]
                if (v > maxValue) {
                    maxValue = v
                    maxIdx = indices[j]
                    maxJ = j
                }
                j += 1
            }

            // 如果最大的非零元素非正且存在未激活的元素，则找到第一个零元素的索引
            if (maxValue <= 0.0 && na < size) {
                if (maxValue == 0.0) {
                    // 如果在maxIdx之前存在未激活的元素，则找到它并返回其索引
                    if (maxJ < maxIdx) {
                        var k = 0
                        while (k < maxJ && indices[k] == k) {
                            k += 1
                        }
                        maxIdx = k
                    }
                } else {
                    // 如果最大的非零值为负数，则找到并返回第一个未激活的索引
                    var k = 0
                    while (k < na && indices[k] == k) {
                        k += 1
                    }
                    maxIdx = k
                }
            }

            maxIdx
        }
    }

    /**
     * 根据给定的索引创建向量的切片。
     * @param selectedIndices 无序的索引列表，用于指定切片中的元素。
     *                        注意：这不会进行边界检查。
     * @return 新的SparseVector，其中的值按照给定索引的顺序排列。
     *
     * 注意：在将其公开之前，需要讨论该API。如果我们有一个假设索引已排序的版本，应该进行优化。
     */
    /*private fun slice(selectedIndices: IntArray): SparseVector  {
        var currentIdx = 0
        val (sliceInds, sliceVals) = selectedIndices.flatMap { origIdx ->
            val iIdx = Arrays.binarySearch(this.indices, origIdx)
            val i_v = if (iIdx >= 0) {
                Iterator((currentIdx, this.values[iIdx]))
            } else {
                Iterator()
            }
            currentIdx += 1
            i_v
        }.unzip
        return SparseVector(selectedIndices.size, sliceInds.toArray, sliceVals.toArray)
    }*/

    // vector * scalar
     override operator fun times(scalar: Float): Vector = SparseVector(size, indices, this.map { it * scalar }.toDoubleArray())

     override operator fun div(f: Float): Vector {
        return times(1 / f)
    }

    /**
     * Dot product of 2 vectors.
     */
     override operator fun times(vector: Vector): Double {
        require(values.size == vector.size) { "Dot product requires that both vectors are the same length" }

        return values.foldIndexed(0.0) { index, acc, t ->
            acc + t * vector[index]
        }
    }

     override operator fun plus(vector: Vector): Vector {
        require(values.size == vector.size) { "Addition requires that both vectors are the same length" }

        return SparseVector(
            size, indices,
            values.mapIndexed { index, i ->
                this[index] + vector[index]
            }.toDoubleArray()
        )
    }

     override operator fun minus(vector: Vector): Vector {
        require(values.size == vector.size) { "Subtraction requires that both vectors are the same length" }
        return SparseVector(
            size, indices,
            values.mapIndexed { index, i ->
                this[index] - vector[index]
            }.toDoubleArray()
        )
    }

     override fun norm(): Double {
        return sqrt(
            values.fold(0.0) { acc, fl ->
                acc + fl * fl
            }
        )
    }


    companion object{
        /** Max number of nonzero entries used in computing hash code. */
        private val MAX_HASH_NNZ = 128
    }
}